
#include "xdns-server-manager-impl.h"
#include "xdns-server-selector.h"
#include "xdns-server-state.h"
#include <assert.h>

using namespace std;

const long XDNS_MAX_QUERY_ID = numeric_limits<long>::max();

XdnsServerManagerImpl::XdnsServerManagerImpl(XdnsServerSelector* selector,
                                             vector<XdnsServerState*>& states)
: XdnsServerManager(),
  m_nextQueryId(XDNS_MAX_QUERY_ID),
  m_selector(selector),
  m_states(states),
  m_queries()
{
    assert(m_selector != NULL);
    assert(!(m_states.empty()));
    vector<XdnsServerState*>::iterator iter = m_states.begin();
    vector<XdnsServerState*>::iterator end = m_states.end();
    for (; iter != end; ++iter)
    {
        assert((*iter) != NULL);
    }
}

XdnsServerManagerImpl::~XdnsServerManagerImpl()
{
    map<long, XdnsQueryState>::iterator queryIter = m_queries.begin();
    map<long, XdnsQueryState>::iterator queryEnd = m_queries.end();
    for (; queryIter != queryEnd; ++queryIter)
    {
        XdnsQuery* query = queryIter->second.m_query;
        assert(query != NULL);
        delete query;
    }
    m_queries.clear();

    if (m_selector != NULL)
    {
        XdnsServerSelector::Release(m_selector);
        m_selector = NULL;
    }

    vector<XdnsServerState*>::iterator serverStateIter = m_states.begin();
    vector<XdnsServerState*>::iterator serverStateEnd = m_states.end();
    for (; serverStateIter != serverStateEnd; ++serverStateIter)
    {
        XdnsServerState* state = *serverStateIter;
        assert(state != NULL);
        XdnsServerState::Release(state);
    }
    m_states.clear();
}

XdnsError
XdnsServerManagerImpl::SendQuery(XdnsQuery* query)
{
    if (NULL == query)
    {
        return XE_Inval;
    }

    // Retrieve a valid ID for the query.
    long newQueryId = 0;
    map<long, XdnsQuery*>::iterator newIter = m_queries.end();
    XdnsError error = GetValidQueryId(newQueryId, newIter);
    if (error != XE_Ok)
    {
        return error;
    }

    XdnsServerState* selectedState = NULL;
    assert(m_selector != NULL);
    error = m_selector->SelectState(NULL, &selectedState);
    if (error != XE_Ok)
    {
        // TODO: Handle the error XE_NoEnt for retry.
        return error;
    }

    error = selectedState->SendQuery(newQueryId, *query);
    if (error != XE_Ok)
    {
        /* TODO: Pay attention to the sychronous dead loop when
                 handling retry.
        */
        return error;
    }

    XdnsQueryState queryState(query);
    m_queries.insert(newIter, make_pair(newQueryId, queryiState));
    if (queryId != NULL)
    {
        *queryId = newQueryId;
    }
    return XE_Ok;
}

void
XdnsServerManagerImpl::CancelQuery(long queryId)
{
    map<long, XdnsQueryState>::iterator iter = m_queries.find(queryId);
    if (iter != m_queries.end())
    {
        XdnsQuery* query = iter->second.m_query;
        assert(query != NULL);
        delete query;
        m_queries.erase(iter);
    }
}

XdnsError
XdnsServerManagerImpl::GetProcessingFds(int* nfds,
                                        fd_set* readfds,
                                        fd_set* writefds)
{
    // gupeng
}

XdnsError
XdnsServerManagerImpl::Process(fd_set* readfds,
                               fd_set* writefds)
{
    // gupeng
}

/*! Retrieve a new valid query ID.

    @param queryId On return a valid query ID.
    @param iter On return an iterator used to insert the query.
    @return XE_Ok if success,
            XE_NoEnt if the query ID is collided with IDs in map.
*/
XdnsError
XdnsHandlerImpl::GetValidQueryId(long& queryId,
                                 map<long, XdnsQuery*>::iterator& iter)
{
    long newQueryId = NextQueryId();
    map<long, XdnsQuery*>::iterator newIter = m_queries.lower_bound(newQueryId);
    if ((newIter != m_queries.end()) &&
        (newIter->first == newQueryId))
    {
        /* The new query ID is present, reset the next query ID and retrieve
           another one.
        */
        m_nextQueryId = XDNS_MAX_QUERY_ID;
        newQueryId = NextQueryId();
        newIter = m_queries.lower_bound(newQueryId);
        if ((newIter != m_queries.end()) &&
           (newIter->first == newQueryId))
        {
            return XE_NoEnt;
        }
    }

    queryId = newQueryId;
    iter = newIter;
    return XE_Ok;
}

long
XdnsHandlerImpl::NextQueryId()
{
    return (m_nextQueryId--);
}

